package be.rivendale.mathematics;

public interface Vector {
    /**
     * The <a href="http://en.wikipedia.org/wiki/Zero_vector">zero or null vector</a>.
     */
    Vector ZERO_VECTOR = new Triple(0, 0, 0);

	/**
	 * Retrieves the x coordinate of this vector.
	 * @return The x coordinate of this vector.
	 */
	double getX();

	/**
	 * Retrieves the y coordinate of this vector.
	 * @return The y coordinate of this vector.
	 */
    double getY();

	/**
	 * Retrieves the z coordinate of this vector.
	 * @return The z coordinate of this vector.
	 */
    double getZ();

    /**
     * Calculates the normalized vector (which is a unit vector) of this vector
     * see http://en.wikipedia.org/wiki/Unit_vector
     * The state of this vector is left unchanged.
     * @return The normalized vector of this vector.
     */
    Vector normalize();

    /**
     * Divides this vector by the specified scalar.
     * Division of a vector by a scalar is done by dividing it's x, y and z values by that scalar.
     * The state of this vector is left unchanged.
     * @param scalarDivisor The value to act as a divisor for this vector.
     * @return The quotient vector of this division.
     */
    Vector divide(double scalarDivisor);

    /**
     * Calculates the length or magnitude of this vector.
     * The length of a vector in 3D space is <code>sqrt(x^2 + y^2 + z^2)</code>
     * @return The length of this vector.
     */
    double length();

    /**
     * Returns the cross product of the two vectors.
     * <code>a x b</code>
     * see http://en.wikipedia.org/wiki/Cross_product
     * The state of either two vectors is left unchanged.
     * @param vector The vector to calculate the cross product with, together with this vector
     * @return The cross product vector of the two vectors involved.
     */
    Vector crossProduct(Vector vector);

    /**
     * Returns the dot product of the two vectors involved.
     * see http://en.wikipedia.org/wiki/Dot_product
     * Both vectors involved are left unchanged.
     * @param vector The second vector to create the dot product with
     * @return The scalar value that represents the dot product between the two vectors.
     */
    double dotProduct(Vector vector);

    /**
     * Returns the angle between the two vectors.
     * The angle between two vectors is determined by this formula:
     * <code>a.b = |a|*|b|*cos(r)</code>
     * where <code>a.b</code> is the {@link #dotProduct(Vector) dot product} between the two vectors,
     * <code>|a| and |b|</code> are the {@link #length() lengths or magnitudes} of both vectors
     * and <code>cos(r)</code> is the cosine of the angle 'r' in radians between both vectors
     * Both vectors involved are left unchanged.
     * @param vector The second vector to use when calculating the angle.
     * @return The angle between this vector and the one specified.
     */
    double angle(Vector vector);

    /**
     * Determines if this vector equals the zero vector (0, 0, 0)
     * @return true if the vector equals the zero vector.
     */
    boolean isZeroVector();

    /**
     * Checks if this vector is normalized.
     * A normalized vector is a vector of length 1
     * @return True if this vector is normalized.
     */
    boolean isNormalized();

    /**
     * Returns a new vector that is the result of multiplying this vector with the specified scalar.
     * This vector is left unchanged.
     * @param scalarValue The scalar value by which to multiply this vector.
     * @return The multiplication of this vector with the specified scalar.
     */
    Vector multiply(double scalarValue);

    /**
     * Checks if two vectors are perpendicular to eachother
     * @param vector The vector to check if it's perpendicular to this one
     * @return true if both vectors are perpendicular to eachother.
     */
    boolean isPerpendicularTo(Vector vector);

    /**
     * Checks if two vectors are parallel to eachother.
     * In other words: it checks if the two vectors will never cross eachother.
     * @param vector The vector that is tested to be parallel to this vector.
     * @return True if the two vectors are parallel. False when they are not.
     */
    boolean isParallelTo(Vector vector);

    Vector add(Vector vector);

    /**
     * Computes the <a href="http://en.wikipedia.org/wiki/Triple_product#Scalar_triple_product">scalar triple product</a> of this vector with two other vectors.
     * <p>The scalar triple product is defined as the dot product of one of the vectors with the cross product of the other two.
     * The order in which the parameters are supplied is not important since this operation is commutative.</p>
     * @param b The second vector to use. (vector a is this object itself)
     * @param c The third vector to use.
     * @return The scalar triple product of three vectors, which is a scalar.
     */
    double scalarTripleProduct(Vector b, Vector c);

	/**
	 * Computes the projection of this vector onto the specified vector.
	 * <p>This operation is not commutative (<code>a.project(b) != b.project(a))</code>, as the new vector will be projected on the passed in vector. This causes
	 * the new vector (the projection result) to be parallel to the specified vector and not to this vector.</p>
	 * @param vectorOnWhichToProject The vector on which to project.
	 * @return A new vector that is the result of projecting this vector on the specified vector.
	 */
	Vector project(Vector vectorOnWhichToProject);

    /**
     * Returns this vector as a point instance.
     * This is the correct way to convert a vector to a point.
     * @return A point, that has the same x, y and z values as the vector.
     */
    Point asPoint();

    /**
     * Creates an vector that is the inverse of this vector.
     * The inversed vector points exactly to the opposite direction.
     * The state of this vector is left unchanged.
     * @return A vector inserse to this vector.
     */
    Vector invert();
}
